package api

import (
	"certificate-manager/pkg/app"
	"certificate-manager/pkg/constant"
	"certificate-manager/pkg/e"
	"certificate-manager/pkg/log"
	utils "certificate-manager/pkg/utils"
	v1 "certificate-manager/routers/api/v1"
	"certificate-manager/thirdparty/acmee"
	"certificate-manager/thirdparty/redisc"
	"fmt"
	"github.com/astaxie/beego/validation"
	"github.com/gin-gonic/gin"
	"go.uber.org/zap"
	"net/http"
	"strings"
	"time"
)

// SyncCaFileFromRedis 根据Redis中数据，刷新存储文件
func SyncCaFileFromRedis(c *gin.Context) {
	appG := app.Gin{C: c}
	valid := validation.Validation{}

	var certificateFileVo v1.CertificateFileVo
	err := c.ShouldBindQuery(&certificateFileVo)
	if err != nil {
		log.Logger.Error("【SyncCaFileFromRedis】param error.", zap.Any("err", fmt.Errorf("param error")), zap.Any("func", utils.RunFuncName()))
		appG.Response(http.StatusOK, e.InvalidParams, nil)
		return
	}

	ok, _ := valid.Valid(&certificateFileVo)
	if !ok {
		log.Logger.Error("【SyncCaFileFromRedis】param error.", zap.Any("err", fmt.Errorf("param error")), zap.Any("func", utils.RunFuncName()))
		appG.Response(http.StatusOK, e.InvalidParams, nil)
		return
	}

	key := certificateFileVo.Key

	redisKey := "ssl:dn:" + key

	//1、获取letsencrypt账号信息
	redisClient, err := redisc.GetRedisClientInstance()
	if err != nil {
		log.Logger.Error("【SyncCaFileFromRedis】failed create new redis client.", zap.Any("err", err), zap.Any("func", utils.RunFuncName()))
		appG.Response(http.StatusOK, e.RedisError, nil)
		return
	}

	//2、判断key是否存在
	exists, err := redisClient.IsRedisKeyExists(redisKey)
	if err != nil {
		log.Logger.Error("【SyncCaFileFromRedis】failed call exist from redis client.", zap.Any("err", err), zap.Any("func", utils.RunFuncName()))
		appG.Response(http.StatusOK, e.RedisError, nil)
		return
	}

	//3、key不存在
	if exists == 0 {
		log.Logger.Info("【SyncCaFileFromRedis】domain not existed.", zap.Any("func", utils.RunFuncName()))
		appG.Response(http.StatusOK, e.DomainHasNotExisted, nil)
		return
	}

	//4、key存在，判断certificate是否存在
	certificateKeyExisted, err := redisClient.IsRedisFieldExists(redisKey, constant.CertificateKey)
	if err != nil {
		log.Logger.Error("【SyncCaFileFromRedis】failed to check the key exists in redis", zap.Any("func", utils.RunFuncName()), zap.Any("err", err))
		appG.Response(http.StatusOK, e.RedisError, nil)
		return
	}

	//5、获取acmeClient
	acmeClient, err := acmee.GetAcmeClientInstance()
	if err != nil {
		log.Logger.Error("【SyncCaFileFromRedis】failed create new acme client.", zap.Any("err", err), zap.Any("func", utils.RunFuncName()))
		appG.Response(http.StatusOK, e.GetAcmeClientError, nil)
		return
	}

	//6、获取acmeAccount
	acmeAccount, err := acmee.GetAcmeAccountInstance(redisClient)
	if err != nil {
		log.Logger.Error("【SyncCaFileFromRedis】failed create new acme account.", zap.Any("err", err), zap.Any("func", utils.RunFuncName()))
		appG.Response(http.StatusOK, e.GetAcmeAccountError, nil)
		return
	}

	//7、如果 key 不存在，则直接申请证书
	if !certificateKeyExisted {
		log.Logger.Sugar().Infof("【SyncCaFileFromRedis】the %v certificate does not exist in redis", strings.TrimPrefix(key, "ssl:dn:"))
		err = acmee.IssuerCertificate(redisKey, acmeClient, acmeAccount)
		if err != nil {
			log.Logger.Error("【SyncCaFileFromRedis】issue certificate error", zap.Any("func", utils.RunFuncName()), zap.Any("err", err))
			appG.Response(http.StatusOK, e.CertificateIssueError, nil)
			return
		}

		appG.Response(http.StatusOK, e.SUCCESS, map[string]string{
			"msg": "certificate不存在，证书签发成功",
		})
		return
	}

	//8、判断notBefore、notAfter是否存在
	values, err := redisClient.FetchRedisKeyAllField(redisKey)
	if err != nil {
		log.Logger.Error("【SyncCaFileFromRedis】failed to get the redis field", zap.Any("key", key), zap.Any("err", err), zap.Any("func", utils.RunFuncName()))
		appG.Response(http.StatusOK, e.RedisError, nil)
		return
	}

	_, notBeforeExisted := values[constant.CertificateNotBeforeKey]
	notAfter, notAfterExisted := values[constant.CertificateNotAfterKey]
	if !notBeforeExisted || !notAfterExisted {
		//9、如果notBefore or notAfter不存在，则直接申请证书
		log.Logger.Sugar().Infof("【SyncCaFileFromRedis】the %v notBefore Key and notAfter Key does not exist in redis", strings.TrimPrefix(key, "ssl:dn:"))

		err = acmee.IssuerCertificate(redisKey, acmeClient, acmeAccount)
		if err != nil {
			log.Logger.Error("【SyncCaFileFromRedis】issue certificate error", zap.Any("func", utils.RunFuncName()), zap.Any("err", err))
			appG.Response(http.StatusOK, e.CertificateIssueError, nil)
			return
		}
		appG.Response(http.StatusOK, e.SUCCESS, map[string]string{
			"msg": "notBefore or notAfter 不存在，证书签发成功",
		})
		return
	}

	//10、判断notAfter距离现在时间是否小于30天
	notAfterValue, err := time.Parse("2006-01-02 15:04:05", notAfter)
	if err != nil {
		log.Logger.Error("【SyncCaFileFromRedis】failed to parse the notAfter time", zap.Any("err", err), zap.Any("func", utils.RunFuncName()))
		appG.Response(http.StatusOK, e.ParseNotAfterError, nil)
		return
	}

	expireDay := time.Until(notAfterValue).Hours() / 24
	if expireDay < 30 {
		log.Logger.Info("【SyncCaFileFromRedis】renew certificate", zap.String("dn", values["dn"]), zap.String("notAfter", values["notAfter"]))
		err = acmee.IssuerCertificate(redisKey, acmeClient, acmeAccount)
		if err != nil {
			log.Logger.Error("【SyncCaFileFromRedis】issue certificate error", zap.Any("func", utils.RunFuncName()), zap.Any("err", err))
			appG.Response(http.StatusOK, e.CertificateIssueError, nil)
			return
		}
		appG.Response(http.StatusOK, e.SUCCESS, map[string]string{
			"msg": "notAfter时间到期，证书签发成功",
		})
		return
	}

	//11、都为匹配到，则把Redis中的数据查出来，刷新到文件中
	certificateStr, _ := values[constant.CertificateKey]
	err = utils.WriteCertificate(redisKey, []byte(certificateStr))
	if err != nil {
		log.Logger.Error("【SyncCaFileFromRedis】flush certificate file error.", zap.Any("func", utils.RunFuncName()), zap.Any("err", err))
		appG.Response(http.StatusOK, e.FlushCertificateError, nil)
		return
	}

	privateKeyStr, _ := values[constant.PrivateKey]
	err = utils.WritePrivateKey(redisKey, []byte(privateKeyStr))
	if err != nil {
		log.Logger.Error("【SyncCaFileFromRedis】flush privatekey file error.", zap.Any("func", utils.RunFuncName()), zap.Any("err", err))
		appG.Response(http.StatusOK, e.FlushPrivateKeyError, nil)
		return
	}

	log.Logger.Info("【SyncCaFileFromRedis】sync success.")
	appG.Response(http.StatusOK, e.SUCCESS, map[string]string{
		"msg": "执行成功",
	})

}
